//创建WxRequest类
//通过类的封装 会让代码更加具有复用性
//也可以方便添加新的属性和方法

class WxRequest {
  // 定义实例属性，用来设置默认请求参数
  defaults = {
    baseURL: '', //公共的请求地址
    url: '', //接口的请求路径
    data: '', //请求参数
    method: 'GET', //默认的请求方法
    header: {
      'Content-Type': 'application/json' //请求头
    },
    timeout: 6000, //默认的超时长，小程序默认的超时长时1分钟
    isLoading: true //是否使用默认的 Loading
  }

  // 配置请求拦截器
  // 注意事项 在发送请求时，还需要区分是否通过实例调用拦截器？
  // 1 没有通过实例调用拦截器，需要调用默认拦截器，在默认拦截器中，需要将请求参数进行返回。
  // 2 通过实例调用拦截器，那么实例调用拦截器会覆盖默认拦截器，然后将新增或者修改的请求参数返回
  interceptors = {
    // 请求拦截器 在请求发送之前 对请求参数进行新增或者删除？
    request: (config) => {
      console.log('requestrequest', config)
      return config
    },
    // 响应拦截器 对服务器响应数据之后进行数据逻辑处理
    response: (response) => {
      console.log('response', response)

      return response
    }
  }
  // 7. 定义一个数组队列 初始值需要是一个空数组 用来存储请求对列标识
  queue = []

  //用于创建和初始化类的属性和方法
  //在实例化传入参数时 会被constructor接受
  constructor(params = {}) {
    console.log('this.defaultsthis.defaults', this.defaults)

    this.defaults = Object.assign({}, this.defaults, params)
    console.log('sdfdfsdf', params, this.defaults)
  }

  //request 实例方法接收一个对象类型参数
  //属性值 wx.request 方法调用时传递的参数保持一致
  request(options) {
    //8.3 如果有新的请求就 清除上一个定时器
    if (this.timerid) {
      clearTimeout(this.timerid)
    }

    console.log('options', options)
    // 9.1
    if (options.isLoading && options.method !== 'UPLOAD') {
      // 7.1 判断queue 队列 是否为空 如果是空 就显示loading
      // 如果不是空 就不显示loadin
      if (this.queue.length === 0) {
        wx.showLoading({ title: '在在加载' })
      }

      // 7.2 然后在立即向 queue 数组队列中添加请求标示
      // 每一个标识是一个请求 标识时自定义的
      this.queue.push('request')
    }

    //注意:需要先合并完整的请求地址 apiUrl + url
    options.url = this.defaults.baseURL + options.url
    //合并请求参数
    options = { ...this.defaults, ...options }

    //5 在请求发送之前，调用请求拦截器， 新增和修改 请求参数
    options = this.interceptors.request(options)

    //需要使用 Promise封装 wx.request 处理异步请求
    return new Promise((resolve, reject) => {
      if (options.method === 'UPLOAD') {
        wx.uploadFile({
          ...options,
          success: (res) => {
            res.data = res.data ? JSON.parse(res.data) : null
            const mergeRes = Object.assign({}, res, { config: options, isSuccess: true })
            resolve(this.interceptors.response(mergeRes))
          },
          fail: (err) => {
            const mergeErr = Object.assign({}, err, {
              config: options,
              isSuccess: false
            })
            reject(this.interceptors.response(mergeErr))
          }
        })
      } else {
        wx.request({
          ...options,
          success: (res) => {
            //5.1不管是成功还是失败响应，都要调用响应拦截器
            //5.2然后在通过resolve(res)抛出
            //5.3再给响应拦截器传递参数时，需要将请求参数也一起传递。
            //5.4 方便进行代码的调试或者进行其他逻辑处理，需要先合并参数
            //5.5 在传递给响应拦截器

            //6.0 在合并的时候 追加一个参数isSuccess ？ success：fail
            const mergeRes = Object.assign({}, res, {
              config: options,
              isSuccess: true
            })
            resolve(this.interceptors.response(mergeRes))
          },
          fail: (err) => {
            //5.1不管是成功还是失败响应，都要调用响应拦截器
            const mergeErr = Object.assign({}, err, {
              config: options,
              isSuccess: false
            })

            console.log('fail2', mergeErr, options)
            console.log('failw', err)

            reject(this.interceptors.response(mergeErr))
          },
          complete: (res) => {
            if (options.isLoading) {
              //不管成功或者失败都会执行
              // 7.2 每一次从queue 队列中删除一个标识
              // 7.3 在删除标识后名需要判断目前queue数组是否为空，如果为空 说名并发请求完成了 就需要隐藏wx.hideLoading()
              this.queue.pop()
              //8.1 a接口刚刚请求完 b接口依赖a b就要立马请求
              if (this.queue.length === 0) {
                this.queue.push('request')
              }
              //8.2 b请求会立马执行，定时器现在还不会执行 需要在清空一下定时器
              this.timerid = setTimeout(() => {
                this.queue.pop()
                if (this.queue.length === 0) {
                  wx.hideLoading()
                  clearTimeout(this.timerid)
                }
              }, 10)
            }
          }
        })
      }
    })
  }

  //3.0 封装GET
  get(url, data = {}, config = {}) {
    return this.request(Object.assign({ url, data, method: 'GET' }, config))
  }
  //3.0 封装DELETE
  deleta(url, data = {}, config = {}) {
    return this.request(Object.assign({ url, data, method: 'DELETE' }, config))
  }
  //3.0 封装POST
  post(url, data = {}, config = {}) {
    return this.request(Object.assign({ url, data, method: 'POST' }, config))
  }
  //3.0 封装PUT
  put(url, data = {}, config = {}) {
    return this.request(Object.assign({ url, data, method: 'PUT' }, config))
  }
  //3.0 封装all处理并发请求
  all(...primise) {
    console.log('primise', primise)
    return Promise.all(primise)
  }
  //3.0 封装 wx.uploadFile 进行封装
  //url upload 文件上传地址 接口地址
  //filePath 要上传的文件资源路径
  //name = 'file' 文件对于的key
  //config = {} 其他配置项
  upload(url, filePath, name = 'file', config = {}) {
    return this.request(Object.assign({ url, filePath, name, method: 'UPLOAD' }, config))
  }
}

// -------测试代码--------------
// 对 WxRequest 进行实例化
const instance = new WxRequest({
  baseURL: 'https://gmall-prod.atguigu.cn/mall-api',
  timeout: 3000,
  isLoading: false
})

// 配置请求拦截器
instance.interceptors.request = (config) => {
  //在请求前做一些添加、修改请求参数的操作
  // const token = wx.getStorageSync('token')
  // if (token) {
  //     config.header['token'] = token
  // }

  return config
}
// //配置响应拦截器
instance.interceptors.response = (response) => {
  // 从 response 中解构 isSuccess
  console.log('response中解构', response)
  const { isSuccess, data } = response
  // 如果 isSuccess 为 false，说明执行了 fail 回调函数
  // 这时候就说明网络异常，需要给用户提示网络异常
  if (!isSuccess) {
    wx.showToast({
      title: '网络异常请重试',
      icon: 'error'
    })
    return response
  }

  return data
}

//将WxRequest 实例 进行暴露出去，方便在其他文件中进行使用
export default instance
